#include "volume.h"

#define MAXNB qint64(9000)

void Volume::setMemorySize(int m) { m_volInterface->setMemorySize(m); }

QImage Volume::histogramImage1D() { return m_histogramImage1D; }
QImage Volume::histogramImage2D() { return m_histogramImage2D; }

Volume::Volume(QObject *parent) : QThread(parent)
{
  m_log = 0;

#ifdef Q_OS_WIN32
  QString plugindir = qApp->applicationDirPath() + QDir::separator() + "readerplugins";
  QString pluginflnm = plugindir + QDir::separator() + "blockreader.dll";
#endif
#ifdef Q_OS_MACX
  // look in drishti.app/renderplugins
  QString sep = QDir::separator();
  QString plugindir = qApp->applicationDirPath()+sep+".."+sep+".."+sep+"readerplugins";
  QString pluginflnm = plugindir + QDir::separator() + "libblockreader.dylib";
#endif



  QPluginLoader pluginLoader(pluginflnm);
  QObject *plugin = pluginLoader.instance();

  if (plugin)
    m_volInterface = qobject_cast<VolInterface *>(plugin);
  else
    QMessageBox::information(0, "", "Cannot load VolInterface plugin");
  
  if (! m_volInterface)
    {
      QMessageBox::information(0, "Error", "Cannot load plugin");
      return;
    }

  m_blockList.clear();

  m_currMaxBlocks = 32;
  m_maxLoadBlocks = 2048;
  m_loadingBlocks = false;

  m_histImageData1D = new unsigned char[256*256*4];
  m_histImageData2D = new unsigned char[256*256*4];
  m_histogramImage1D = QImage(256, 256, QImage::Format_RGB32);
  m_histogramImage2D = QImage(256, 256, QImage::Format_RGB32);
}

Volume::~Volume()
{
  m_blockList.clear();
  m_compactGrid.clear();

  for(int i=0; i<m_volData.count(); i++)
    delete [] m_volData[i];
  for(int i=0; i<m_auxData.count(); i++)
    delete [] m_auxData[i];
}

void
Volume::setLogger(QTextEdit *log)
{
  m_log = log;
  m_log->insertPlainText("volume logger set\n");
}

void
Volume::setMaxLoadBlocks(int mb)
{
  m_maxLoadBlocks = mb;
  m_log->insertPlainText(QString("Max Blocks set to %1\n").arg(m_maxLoadBlocks));
}


void Volume::init() { m_volInterface->init(); }
void Volume:: setFilename(QList<QString> flnm)
{
  m_nVolumes = flnm.count();
  m_volInterface->setFilename(flnm);
  m_volInterface->blockedGridSize(m_bd, m_br, m_bc);
  m_nblocks = m_bd*m_br*m_bc;

  generateHistogramImage();
}

void
Volume::setLodLevel(int s)
{
  clearBlocks();
  m_volInterface->setLodLevel(s);
  m_volInterface->blockedGridSize(m_bd, m_br, m_bc);
  m_nblocks = m_bd*m_br*m_bc;
}

void Volume:: blockedGridSize(int& d, int& w, int& h)
{
  d=m_bd;
  w=m_br;
  h=m_bc;
}

//void Volume:: blockedGridSize(int& d, int& w, int& h) { m_volInterface->blockedGridSize(d, w, h); }
int Volume::blockSize() { return m_volInterface->blockSize(); }
void Volume::setTextureSize(int sz) { m_volInterface->setTextureSize(sz); }
void Volume::setGridSize(int d, int w, int h) { m_volInterface->setGridSize(d, w, h); }
void Volume::setUserLimits(Vec bmin, Vec bmax) { m_volInterface->setUserLimits(bmin, bmax); }
void Volume::gridSize(int& d, int& w, int& h) { m_volInterface->gridSize(d, w, h); }
void Volume::gridSize(int l, int& d, int& w, int& h) { m_volInterface->gridSize(l, d, w, h); }
void Volume::p2gridSize(int& d, int& w, int& h) { m_volInterface->p2gridSize(d, w, h); }
void Volume::p2blockedGridSize(int& d, int& w, int& h) { m_volInterface->p2blockedGridSize(d, w, h); }
uchar* Volume::blockLevel() { return m_volInterface->blockLevel(); }


void
Volume:: compactGridSize(int& d, int& w, int& h)
{
  Vec cgs = m_compactGrid[0];
  d = cgs.x;
  w = cgs.y;
  h = cgs.z;
  m_compactGrid.removeFirst();
  //m_volInterface->compactGridSize(d, w, h);
}

uchar*
Volume:: blockedData()
{
  //return m_volInterface->blockedData();
  uchar* v = m_volData.first();
  m_volData.removeFirst();
  return v;
}

uchar*
Volume:: auxData()
{
  //return m_volInterface->auxData();
  uchar* a = m_auxData.first();
  m_auxData.removeFirst();
  return a;
}

int Volume:: minLod() { return m_volInterface->minLod(); }
int Volume:: minLod(int isoval) { return m_volInterface->minLod(isoval); }
int Volume:: minLod(uchar *lut) { return m_volInterface->minLod(lut); }

bool
Volume:: initIdentifyValidBlocks(int isoval)
{
  return m_volInterface->initIdentifyValidBlocks(isoval);
}

bool
Volume:: initIdentifyValidBlocks(uchar *lut)
{
  return m_volInterface->initIdentifyValidBlocks(lut);
}

bool
Volume::getNextBlockLot(int& cd, int& cr, int& cc,
			uchar* &volData,
			uchar* &auxData)
{
  m_mutex.lock();

  if (m_compactGrid.count() == 0)
    {
      m_mutex.unlock();
      return false;
    }

  Vec cgs = m_compactGrid[0];
  cd = cgs.x;
  cr = cgs.y;
  cc = cgs.z;

  volData = m_volData.first();
  auxData = m_auxData.first();

  m_compactGrid.removeFirst();
  m_volData.removeFirst();
  m_auxData.removeFirst();

  m_mutex.unlock();
  return true;
}

void
Volume::clearBlocks()
{
  QMutexLocker locker(&m_mutex);

  m_blockList.clear();
  m_compactGrid.clear();

  for(int i=0; i<m_volData.count(); i++)
    delete [] m_volData[i];
  for(int i=0; i<m_auxData.count(); i++)
    delete [] m_auxData[i];

  m_volData.clear();
  m_auxData.clear();

  //m_log->insertPlainText("clear all blocks\n");
  m_currMaxBlocks = 32;
}

void
Volume::loadAllValidBlocks(int s)
{
  //m_log->insertPlainText(QString("volume : load all valid blocks\n"));

  clearBlocks();
  
  QMutexLocker locker(&m_mutex);

  m_blockList.insert(-1, s);

  if (!isRunning())
    start();
  else
    m_condition.wakeAll();
}

void
Volume::loadBlocks(QList<Vec> bl, int s)
{
  //m_log->insertPlainText(QString("loadBlocks %1\n").arg(bl.count()));

  m_currMaxBlocks = qMin(m_maxLoadBlocks, m_currMaxBlocks+32);

  QMutexLocker locker(&m_mutex);

//----------------------------------------------------

  foreach(Vec blk, bl)
    {
      int x = blk.x;
      int y = blk.y;
      int z = blk.z;
      qint64 bno;
      bno = x*MAXNB*MAXNB;
      bno += y*MAXNB;
      bno += z;
      m_blockList.insert(bno, s);
    }

  if (!isRunning())
    start();
  else
    m_condition.wakeAll();
//----------------------------------------------------
}

void
Volume::run()
{
  forever
    {
      if (m_blockList.count() == 0)
	{
	  m_mutex.lock();
	  m_condition.wait(&m_mutex);
	}
      else
	m_mutex.lock();
      
      QList<Vec> blocks;
      QList<qint64> bno = m_blockList.keys();

      int slevel = 0;
      if (bno.count() > 0)
	slevel = m_blockList.value(bno[0]);

      if (bno.count() == 1 && bno[0] == -1)
	{
	  m_blockList.clear();
	  blocks << Vec(0,0,0);
	  m_mutex.unlock();
	  m_volInterface->loadAllValidBlocks(slevel);
	}
      else
	{
	  for(int i=0; i<qMin(m_currMaxBlocks, bno.count()); i++)
	    //for(int i=0; i<bno.count(); i++)
	    {
	      int x = bno[i]/(MAXNB*MAXNB);
	      int y = (bno[i] - x*MAXNB*MAXNB)/MAXNB;
	      int z = bno[i] - x*MAXNB*MAXNB -y*MAXNB;
	      
	      blocks << Vec(x,y,z);
	      
	      m_blockList.remove(bno[i]);
	    }
	  m_mutex.unlock();
	  
	  if (blocks.count() > 0)
	    m_volInterface->loadBlocks(blocks,  slevel);
	}

      //--------------------------------------      
      if (blocks.count() > 0)
	{
	  QMutexLocker locker(&m_mutex);

	  int cd, cr, cc;
	  m_volInterface->compactGridSize(cd, cr, cc);
	  m_compactGrid.append(Vec(cd, cr, cc));      
//	  m_log->insertPlainText(QString("new grid : %1 %2 %3\n").\
//				 arg(cd).arg(cr).arg(cc));


	  uchar *v = new uchar[m_nVolumes*cd*cr*cc];
	  memcpy(v, m_volInterface->blockedData(), m_nVolumes*cc*cr*cd);
	  m_volData.append(v);
      
	  uchar *a = new uchar[4*m_nblocks];
	  memcpy(a, m_volInterface->auxData(), 4*m_nblocks);
	  m_auxData.append(a);
	}
      //--------------------------------------
      
      if (blocks.count() > 0)
	{
	  qApp->processEvents();
	  emit readDone();
	}
      
      //m_log->insertPlainText(QString("%1(%2) ").arg(m_volData.count()).arg(m_currMaxBlocks));      
      
//      if (m_blockList.count() > 0)
//	m_condition.wakeAll();
    }
}

uchar*
Volume::wholeVolume(int s)
{
  return m_volInterface->wholeVolume(s);
}

uchar*
Volume::loadSingleBlock(int i, int j, int k, int s)
{
  return m_volInterface->loadSingleBlock(i, j, k, s);
}

void
Volume:: setIsovalue(int s)
{
  m_volInterface->setIsovalue(s);
}

void
Volume::generateHistogramImage()
{
  memset(m_histImageData1D, 256*256*4, 0);
  memset(m_histImageData2D, 256*256*4, 0);

  int *hist2D = m_volInterface->get2dHistogram();
  if (hist2D)
    {
      for (int i=0; i<256*256; i++)
	{
	  m_histImageData2D[4*i + 3] = 255;
	  m_histImageData2D[4*i + 0] = hist2D[i];
	  m_histImageData2D[4*i + 1] = hist2D[i];
	  m_histImageData2D[4*i + 2] = hist2D[i];
	}
    }
  m_histogramImage2D = QImage(m_histImageData2D, 256, 256, QImage::Format_ARGB32);
  m_histogramImage2D = m_histogramImage2D.mirrored();  


  int *hist1D = m_volInterface->get1dHistogram();
  memset(m_histImageData1D, 0, 4*256*256);
  if (hist1D)
    {
      for (int i=0; i<256; i++)
	{
	  for (int j=0; j<256; j++)
	    {
	      int idx = 256*j + i;
	      m_histImageData1D[4*idx + 3] = 255;
	    }
	  
	  int h = hist1D[i];
	  for (int j=0; j<h; j++)
	    {
	      int idx = 256*j + i;
	      m_histImageData1D[4*idx + 0] = 255*j/h;
	      m_histImageData1D[4*idx + 1] = 255*j/h;
	      m_histImageData1D[4*idx + 2] = 255*j/h;
	    }
	}
    }
  m_histogramImage1D = QImage(m_histImageData1D, 256, 256, QImage::Format_ARGB32);
  m_histogramImage1D = m_histogramImage1D.mirrored();  
}
